/*
* Copyright 2012-2015 Cenote GmbH.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.cenote.jasperstarter;
import de.cenote.jasperstarter.types.AskFilter;
import de.cenote.jasperstarter.types.Command;
import de.cenote.jasperstarter.types.DsType;
import de.cenote.jasperstarter.types.Dest;
import de.cenote.jasperstarter.types.OutputFormat;
import de.cenote.tools.classpath.ApplicationClasspath;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRParameter;
import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Argument;
import net.sourceforge.argparse4j.inf.ArgumentGroup;
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.FeatureControl;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import net.sourceforge.argparse4j.inf.Subparsers;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.WildcardFileFilter;
/**
*
* @author Volker Voßkämper <vvo at cenote.de>
* @version $Revision: 349bcea5768c:59 branch:default $
*/
public class App {
private Namespace namespace = null;
private Map<String, Argument> allArguments = null;
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Config config = new Config();
App app = new App();
// create the command line parser
ArgumentParser parser = app.createArgumentParser(config);
if (args.length == 0) {
System.out.println(parser.formatUsage());
System.out.println("type: jasperstarter -h to get help");
System.exit(0);
}
try {
app.parseArgumentParser(args, parser, config);
} catch (ArgumentParserException ex) {
parser.handleError(ex);
System.exit(1);
}
if (config.isVerbose()) {
System.out.print("Command line:");
for (String arg : args) {
System.out.print(" " + arg);
}
// @todo: this makes sense only if Config.toString() is overwitten
// System.out.print("\n");
// System.out.println(config);
}
// @todo: main() will not be executed in tests...
// setting locale if given
if (config.hasLocale()) {
Locale.setDefault(config.getLocale());
}
try {
switch (Command.getCommand(config.getCommand())) {
case COMPILE:
case CP:
app.compile(config);
break;
case PROCESS:
case PR:
app.processReport(config);
break;
case LIST_PRINTERS:
case PRINTERS:
case LPR:
app.listPrinters();
break;
case LIST_PARAMETERS:
case PARAMS:
case LPA:
App.listReportParams(config, new File(config.getInput()).getAbsoluteFile());
break;
}
} catch (IllegalArgumentException ex) {
System.err.println(ex.getMessage());
System.exit(1);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
System.exit(1);
} catch (JRException ex) {
System.err.println(ex.getMessage());
System.exit(1);
}
}
private void compile(Config config) {
IllegalArgumentException error = null;
File input = new File(config.getInput());
if (input.isFile()) {
try {
Report report = new Report(config, input);
report.compileToFile();
} catch (IllegalArgumentException ex) {
error = ex;
}
} else if (input.isDirectory()) {
// compile all .jrxml files in this directory
FileFilter fileFilter = new WildcardFileFilter("*.jrxml", IOCase.INSENSITIVE);
File[] files = input.listFiles(fileFilter);
for (File file : files) {
try {
System.out.println("Compiling: \"" + file + "\"");
Report report = new Report(config, file);
report.compileToFile();
} catch (IllegalArgumentException ex) {
error = ex;
}
}
} else {
error = new IllegalArgumentException("Error: not a file: " + input.getName());
}
if (error != null) {
throw error;
}
}
private void processReport(Config config)
throws IllegalArgumentException, InterruptedException, JRException {
// add the jdbc dir to classpath
try {
if (config.hasJdbcDir()) {
File jdbcDir = config.getJdbcDir();
if (config.isVerbose()) {
System.out.println("Using jdbc-dir: " + jdbcDir.getAbsolutePath());
}
ApplicationClasspath.addJars(jdbcDir.getAbsolutePath());
} else {
ApplicationClasspath.addJarsRelative("../jdbc");
}
} catch (IOException ex) {
throw new IllegalArgumentException("Error adding jdbc-dir", ex);
} catch (URISyntaxException ex) {
throw new IllegalArgumentException("Error adding jdbc-dir: \"../jdbc\"", ex);
}
// add optional resources to classpath
if (config.hasResource()) {
try {
if ("".equals(config.getResource())) { // the default
// add the parent of input to classpath
File res = new File(config.getInput()).getAbsoluteFile().getParentFile();
if (res.isDirectory()) {
ApplicationClasspath.add(res);
if (config.isVerbose()) {
System.out.println(
"Added resource \"" + res + "\" to classpath");
}
} else {
throw new IllegalArgumentException(
"Resource path \"" + res + "\" is not a directory");
}
} else {
// add file or dir to classpath
File res = new File(config.getResource());
ApplicationClasspath.add(res);
if (config.isVerbose()) {
System.out.println(
"Added resource \"" + res + "\" to classpath");
}
}
} catch (IOException ex) {
throw new IllegalArgumentException("Error adding resource \""
+ config.getResource() + "\" to classpath", ex);
}
}
File inputFile = new File(config.getInput()).getAbsoluteFile();
if (config.isVerbose()) {
System.out.println("Original input file: " + inputFile.getAbsolutePath());
}
inputFile = locateInputFile(inputFile);
if (config.isVerbose()) {
System.out.println("Using input file: " + inputFile.getAbsolutePath());
}
Report report = new Report(config, inputFile);
report.fill(); // produces visible output file if OutputFormat.jrprint is set
List<OutputFormat> formats = config.getOutputFormats();
Boolean viewIt = false;
Boolean printIt = false;
for (OutputFormat f : formats) {
// OutputFormat.jrprint is handled in fill()
if (OutputFormat.print.equals(f)) {
printIt = true;
} else if (OutputFormat.view.equals(f)) {
viewIt = true;
} else if (OutputFormat.pdf.equals(f)) {
report.exportPdf();
} else if (OutputFormat.docx.equals(f)) {
report.exportDocx();
} else if (OutputFormat.odt.equals(f)) {
report.exportOdt();
} else if (OutputFormat.rtf.equals(f)) {
report.exportRtf();
} else if (OutputFormat.html.equals(f)) {
report.exportHtml();
} else if (OutputFormat.xml.equals(f)) {
report.exportXml();
} else if (OutputFormat.xls.equals(f)) {
report.exportXls();
} else if (OutputFormat.xlsMeta.equals(f)) {
report.exportXlsMeta();
} else if (OutputFormat.xlsx.equals(f)) {
report.exportXlsx();
} else if (OutputFormat.csv.equals(f)) {
report.exportCsv();
} else if (OutputFormat.csvMeta.equals(f)) {
report.exportCsvMeta();
} else if (OutputFormat.ods.equals(f)) {
report.exportOds();
} else if (OutputFormat.pptx.equals(f)) {
report.exportPptx();
} else if (OutputFormat.xhtml.equals(f)) {
report.exportXhtml();
} else if (OutputFormat.jrprint.equals(f)) {
// nothing to do. Option is used in Report.fill()
} else {
throw new IllegalArgumentException("Error output format \"" + f + "\" not implemented!");
}
}
if (viewIt) {
report.view();
} else if (printIt) {
// print directly only if viewer is not activated
report.print();
}
}
/**
*
* @param inputFile file or basename of a JasperReports file
* @return a valid file that is not a directory and has a fileending of
* (jrxml, jasper, jrprint)
*/
private File locateInputFile(File inputFile) {
if (!inputFile.exists()) {
File newInputfile;
// maybe the user omitted the file extension
// first trying .jasper
newInputfile = new File(inputFile.getAbsolutePath() + ".jasper");
if (newInputfile.isFile()) {
inputFile = newInputfile;
}
if (!inputFile.exists()) {
// second trying .jrxml
newInputfile = new File(inputFile.getAbsolutePath() + ".jrxml");
if (newInputfile.isFile()) {
inputFile = newInputfile;
}
}
}
if (!inputFile.exists()) {
throw new IllegalArgumentException("Error: file not found: " + inputFile.getAbsolutePath());
} else if (inputFile.isDirectory()) {
throw new IllegalArgumentException("Error: " + inputFile.getAbsolutePath() + " is a directory, file needed");
}
return inputFile;
}
private void listPrinters() {
PrintService defaultService = PrintServiceLookup.lookupDefaultPrintService();
System.out.println("Default printer:");
System.out.println("-----------------");
System.out.println((defaultService == null) ? "--- not set ---" : defaultService.getName());
System.out.println("");
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
System.out.println("Available printers:");
System.out.println("--------------------");
for (PrintService service : services) {
System.out.println(service.getName());
}
}
private ArgumentParser createArgumentParser(Config config) {
this.allArguments = new HashMap<String, Argument>();
ArgumentParser parser = ArgumentParsers.newArgumentParser("jasperstarter", false, "-", "@")
.version(config.getVersionString());
//ArgumentGroup groupOptions = parser.addArgumentGroup("options");
parser.addArgument("-h", "--help").action(Arguments.help()).help("show this help message and exit");
parser.addArgument("--locale").dest(Dest.LOCALE).metavar("<lang>")
.help("set locale with two-letter ISO-639 code"
+ " or a combination of ISO-639 and ISO-3166 like de_DE");
parser.addArgument("-v", "--verbose").dest(Dest.DEBUG).action(Arguments.storeTrue()).help("display additional messages");
parser.addArgument("-V", "--version").action(Arguments.version()).help("display version information and exit");
Subparsers subparsers = parser.addSubparsers().title("commands").
help("type <cmd> -h to get help on command").metavar("<cmd>").
dest(Dest.COMMAND);
Subparser parserCompile =
subparsers.addParser("compile", true).aliases("cp")
.help("compile reports");
createCompileArguments(parserCompile);
Subparser parserProcess =
subparsers.addParser("process", true).aliases("pr")
.help("view, print or export an existing report");
createProcessArguments(parserProcess);
Subparser parserListPrinters =
subparsers.addParser("list_printers", true).aliases("printers", "lpr")
.help("lists available printers");
Subparser parserListParams =
subparsers.addParser("list_parameters", true).aliases("params", "lpa").
help("list parameters from a given report");
createListParamsArguments(parserListParams);
return parser;
}
private void createCompileArguments(Subparser parser) {
ArgumentGroup groupOptions = parser.addArgumentGroup("options");
groupOptions.addArgument("input").metavar("<input>").dest(Dest.INPUT).required(true).help("input file (.jrxml) or directory");
groupOptions.addArgument("-o").metavar("<output>").dest(Dest.OUTPUT).help("directory or basename of outputfile(s)");
}
private void createListParamsArguments(Subparser parser) {
ArgumentGroup groupOptions = parser.addArgumentGroup("options");
groupOptions.addArgument("input").metavar("<input>").dest(Dest.INPUT).required(true).help("input file (.jrxml) or (.jasper)");
}
private void createProcessArguments(Subparser parser) {
ArgumentGroup groupOptions = parser.addArgumentGroup("options");
groupOptions.addArgument("-f").metavar("<fmt>").dest(Dest.OUTPUT_FORMATS).
required(true).nargs("+").type(Arguments.enumType(OutputFormat.class)).
help("view, print, pdf, rtf, xls, xlsMeta, xlsx, docx, odt, ods, pptx, csv, csvMeta, html, xhtml, xml, jrprint");
groupOptions.addArgument("input").metavar("<input>").dest(Dest.INPUT).required(true).help("input file (.jrxml|.jasper|.jrprint)");
groupOptions.addArgument("-o").metavar("<output>").dest(Dest.OUTPUT).help("directory or basename of outputfile(s)");
//groupOptions.addArgument("-h", "--help").action(Arguments.help()).help("show this help message and exit");
ArgumentGroup groupCompileOptions = parser.addArgumentGroup("compile options");
groupCompileOptions.addArgument("-w", "--write-jasper").
dest(Dest.WRITE_JASPER).action(Arguments.storeTrue()).help("write .jasper file to imput dir if jrxml is processed");
ArgumentGroup groupFillOptions = parser.addArgumentGroup("fill options");
groupFillOptions.addArgument("-a").metavar("<filter>").dest(Dest.ASK)
.type(Arguments.enumType(AskFilter.class)).nargs("?")
.setConst(AskFilter.p)
.help("ask for report parameters. Filter: a, ae, u, ue, p, pe"
+ " (see usage)");
groupFillOptions.addArgument("-P").metavar("<param>").dest(Dest.PARAMS)
.nargs("+").help(
"report parameter: name=value [...]");
groupFillOptions.addArgument("-r").metavar("<resource>").dest(Dest.RESOURCE)
.nargs("?").setConst("").help(
"path to report resource dir or jar file. If <resource> is not"
+ " given the input directory is used.");
ArgumentGroup groupDatasourceOptions = parser.addArgumentGroup("datasource options");
groupDatasourceOptions.addArgument("-t").metavar("<dstype>").dest(Dest.DS_TYPE).
required(false).type(Arguments.enumType(DsType.class)).setDefault(DsType.none).
help("datasource type: none, csv, xml, json, mysql, postgres, oracle, generic (jdbc)");
Argument argDbHost = groupDatasourceOptions.addArgument("-H").metavar("<dbhost>").dest(Dest.DB_HOST).help("database host");
Argument argDbUser = groupDatasourceOptions.addArgument("-u").metavar("<dbuser>").dest(Dest.DB_USER).help("database user");
Argument argDbPasswd = groupDatasourceOptions.addArgument("-p").metavar("<dbpasswd>").dest(Dest.DB_PASSWD).setDefault("").help("database password");
Argument argDbName = groupDatasourceOptions.addArgument("-n").metavar("<dbname>").dest(Dest.DB_NAME).help("database name");
Argument argDbSid = groupDatasourceOptions.addArgument("--db-sid").metavar("<sid>").dest(Dest.DB_SID).help("oracle sid");
Argument argDbPort = groupDatasourceOptions.addArgument("--db-port").metavar("<port>").dest(Dest.DB_PORT).type(Integer.class).help("database port");
Argument argDbDriver = groupDatasourceOptions.addArgument("--db-driver").metavar("<name>").dest(Dest.DB_DRIVER).help("jdbc driver class name for use with type: generic");
Argument argDbUrl = groupDatasourceOptions.addArgument("--db-url").metavar("<jdbcUrl>").dest(Dest.DB_URL).help("jdbc url without user, passwd with type:generic");
groupDatasourceOptions.addArgument("--jdbc-dir").metavar("<dir>").dest(Dest.JDBC_DIR).type(File.class).help("directory where jdbc driver jars are located. Defaults to ./jdbc");
Argument argDataFile = groupDatasourceOptions.addArgument("--data-file").metavar("<file>").dest(Dest.DATA_FILE).type(File.class).help("input file for file based datasource");
groupDatasourceOptions.addArgument("--csv-first-row").metavar("true", "false").dest(Dest.CSV_FIRST_ROW).action(Arguments.storeTrue()).help("first row contains column headers");
Argument argCsvColumns = groupDatasourceOptions.addArgument("--csv-columns").metavar("<list>").dest(Dest.CSV_COLUMNS).help("Comma separated list of column names");
groupDatasourceOptions.addArgument("--csv-record-del").metavar("<delimiter>").dest(Dest.CSV_RECORD_DEL).setDefault(System.getProperty("line.separator")).help("CSV Record Delimiter - defaults to line.separator");
groupDatasourceOptions.addArgument("--csv-field-del").metavar("<delimiter>").dest(Dest.CSV_FIELD_DEL).setDefault(",").help("CSV Field Delimiter - defaults to \",\"");
groupDatasourceOptions.addArgument("--csv-charset").metavar("<charset>").dest(Dest.CSV_CHARSET).setDefault("utf-8").help("CSV charset - defaults to \"utf-8\"");
Argument argXmlXpath = groupDatasourceOptions.addArgument("--xml-xpath").metavar("<xpath>").dest(Dest.XML_XPATH).help("XPath for XML Datasource");
Argument argJsonQuery = groupDatasourceOptions.addArgument("--json-query").metavar("<jsonquery>").dest(Dest.JSON_QUERY).help("JSON query string for JSON Datasource");
ArgumentGroup groupOutputOptions = parser.addArgumentGroup("output options");
groupOutputOptions.addArgument("-N").metavar("<printername>").dest(Dest.PRINTER_NAME).help("name of printer");
groupOutputOptions.addArgument("-d").dest(Dest.WITH_PRINT_DIALOG).action(Arguments.storeTrue()).help("show print dialog when printing");
groupOutputOptions.addArgument("-s").metavar("<reportname>").dest(Dest.REPORT_NAME).help("set internal report/document name when printing");
groupOutputOptions.addArgument("-c").metavar("<copies>").dest(Dest.COPIES)
.type(Integer.class).choices(Arguments.range(1, Integer.MAX_VALUE))
.help("number of copies. Defaults to 1");
groupOutputOptions.addArgument("--out-field-del").metavar("<delimiter>").dest(Dest.OUT_FIELD_DEL).setDefault(",").help("Export CSV (Metadata) Field Delimiter - defaults to \",\"");
groupOutputOptions.addArgument("--out-charset").metavar("<charset>").dest(Dest.OUT_CHARSET).setDefault("utf-8").help("Export CSV (Metadata) Charset - defaults to \"utf-8\"");
allArguments.put(argDbHost.getDest(), argDbHost);
allArguments.put(argDbUser.getDest(), argDbUser);
allArguments.put(argDbPasswd.getDest(), argDbPasswd);
allArguments.put(argDbName.getDest(), argDbName);
allArguments.put(argDbSid.getDest(), argDbSid);
allArguments.put(argDbPort.getDest(), argDbPort);
allArguments.put(argDbDriver.getDest(), argDbDriver);
allArguments.put(argDbUrl.getDest(), argDbUrl);
allArguments.put(argDataFile.getDest(), argDataFile);
allArguments.put(argCsvColumns.getDest(), argCsvColumns);
allArguments.put(argXmlXpath.getDest(), argXmlXpath);
allArguments.put(argJsonQuery.getDest(), argJsonQuery);
}
private void parseArgumentParser(String[] args, ArgumentParser parser, Config config) throws ArgumentParserException {
parser.parseArgs(args, config);
// change some arguments to required depending on db-type
if (config.hasDbType()) {
if (config.getDbType().equals(DsType.none)) {
// nothing to do here
} else if (config.getDbType().equals(DsType.mysql)) {
allArguments.get(Dest.DB_HOST).required(true);
allArguments.get(Dest.DB_USER).required(true);
allArguments.get(Dest.DB_NAME).required(true);
allArguments.get(Dest.DB_PORT).setDefault(DsType.mysql.getPort());
} else if (config.getDbType().equals(DsType.postgres)) {
allArguments.get(Dest.DB_HOST).required(true);
allArguments.get(Dest.DB_USER).required(true);
allArguments.get(Dest.DB_NAME).required(true);
allArguments.get(Dest.DB_PORT).setDefault(DsType.postgres.getPort());
} else if (config.getDbType().equals(DsType.oracle)) {
allArguments.get(Dest.DB_HOST).required(true);
allArguments.get(Dest.DB_USER).required(true);
allArguments.get(Dest.DB_PASSWD).required(true);
allArguments.get(Dest.DB_SID).required(true);
allArguments.get(Dest.DB_PORT).setDefault(DsType.oracle.getPort());
} else if (config.getDbType().equals(DsType.generic)) {
allArguments.get(Dest.DB_DRIVER).required(true);
allArguments.get(Dest.DB_URL).required(true);
} else if (DsType.csv.equals(config.getDbType())) {
allArguments.get(Dest.DATA_FILE).required(true);
if (!config.getCsvFirstRow()) {
allArguments.get(Dest.CSV_COLUMNS).required(true);
}
} else if (DsType.xml.equals(config.getDbType())) {
allArguments.get(Dest.DATA_FILE).required(true);
allArguments.get(Dest.XML_XPATH).required(true);
} else if (DsType.json.equals(config.getDbType())) {
allArguments.get(Dest.DATA_FILE).required(true);
allArguments.get(Dest.JSON_QUERY).required(true);
}
}
// parse again so changed arguments become effectiv
parser.parseArgs(args, config);
}
/**
* @return the namespace
*/
public static void listReportParams(Config config, File input) throws IllegalArgumentException {
boolean all;
Report report = new Report(config, input);
JRParameter[] params = report.getReportParameters();
int maxName = 1;
int maxClassName = 1;
int maxDesc = 1;
all = false; // this is a default for now
// determine proper length of stings for nice alignment
for (JRParameter param : params) {
if (!param.isSystemDefined() || all) {
if (param.getName() != null) {
maxName = Math.max(maxName, param.getName().length());
}
if (param.getValueClassName() != null) {
maxClassName = Math.max(maxClassName, param.getValueClassName().length());
}
if (param.getDescription() != null) {
maxDesc = Math.max(maxDesc, param.getDescription().length());
}
}
}
for (JRParameter param : params) {
if (!param.isSystemDefined() || all) {
System.out.printf("%s %-" + maxName + "s %-" + maxClassName + "s %-" + maxDesc + "s %n",
//(param.isSystemDefined() ? "S" : "U"),
(param.isForPrompting() ? "P" : "N"),
param.getName(),
param.getValueClassName(),
(param.getDescription() != null ? param.getDescription() : ""));
}
}
}
}